//课设LED广告 数据锁存方案
#include <reg51.h>
#include <intrins.h>
#define u8 unsigned char
#define u16	unsigned int

sbit LE1=P1^0;    //锁存片选地址0
sbit LE2=P1^1;    //锁存片选地址1

u8 code T_zhang[32] = {  0x01,0x00,0xF9,0x08,0x09,0x08,0x09,0x10,0x09,0x20,0x79,0x40,0x41,0x00,0x47,0xFE,0x41,0x40,0x79,0x20,
	0x09,0x20,0x09,0x10,0x09,0x08,0x09,0x44,0x51,0x82,0x21,0x00 }; //行扫描 阴码 高位在前 
u8 code T_yun[32] = {  0x10,0x00,0x10,0xFC,0x10,0x00,0xFE,0x00,0x10,0x00,0x7C,0x00,0x11,0xFE,0xFE,0x20,0x10,0x20,0x38,0x40,
	0x34,0x48,0x54,0x84,0x50,0x84,0x91,0xFE,0x10,0x82,0x10,0x02 };
u8 code T_xuan[32] = {  0x00,0x40,0x00,0x20,0xFB,0xFE,0x22,0x02,0x24,0x04,0x21,0xFC,0x20,0x00,0xF9,0xFC,0x21,0x04,0x21,0xFC,
    0x21,0x04,0x39,0xFC,0xE1,0x04,0x40,0x00,0x03,0xFE,0x00,0x00 };
// 封装成函数后字码需存放在ROM,若存放在RAM会出现字符的地址顺序和指针数组中的位置顺序相反的情况。即zhang的起始地址比yun高

u8 TAB_length = 3; // 字符个数
u8 *TAB_addr[] = {&T_zhang,&T_yun,&T_xuan}; // 存放字符阴码行数组地址(单向)

u8 mode=0;            // 显示模式  0横向左移 1纵向上移 2闪烁交替 3静态交替
int stay_ms = 750;	  //内容驻留时长(ms) 可调范围250-1000 (位移间隔时长与闪烁文字交替时长)
bit STAY_CHANGE = 0;  //驻留时长改变标识
int scan_delay =3;	  // 扫描间隔(ms)

void delay_ms(u16 x)
{// 延时
	u8 t;
	while(x--){
		for(t=0;t<100;t++);
	}
}

void row_data_send_byte(u8 datl,u8 datr,u8 datl2,u8 datr2,u8 out_side){
// 锁存器数据传输 分路控制 8位
// datl: 左阵列高4位
// datr: 左阵列低4位
// datl2: 右阵列高4位
// datr2: 右阵列低4位
// out_side: 扫描列的位码

	P2=0x00;	 // 消隐
	P1=0x00;	 //启用锁存器1
	P0=datr;	 // 传入低字节
	P1=0x01;	 //启用锁存器2
	_nop_();_nop_();
	P0=datl; // 传入高字节
	P1=0x02;	 //启用锁存器3
	_nop_();_nop_();
	P0=datr2;	 // 传入低字节
	P1=0x03;	 //启用锁存器4
	_nop_();_nop_();
	P0=datl2;	 // 传入高字节
	P2=out_side;
}

void left_move(){
// 逐列左移	 模式0
	u8 out_side;
	int i;
	int num;
	int offset;
	int time;
	int stay_length  = stay_ms / (scan_delay * 16); //内容驻留循环次数(分子值单位ms)
	u8 l_high_char_half,l_high_char_filled,l_low_char_half,l_low_char_filled,r_high_char_half,r_high_char_filled,r_low_char_half,r_low_char_filled;

	for(num=0;num<TAB_length;num++){    // 循环 字符数组的下标
		for(offset=0;offset<8;offset++){   // 列偏移量循环 单字前8位
			for(time=0;time<stay_length;time++){
				if (mode!=0) return;  // 显示模式切换后退出显示
				if (STAY_CHANGE==1){
					stay_length = stay_ms / (scan_delay * 16);
					STAY_CHANGE = 0;
				}
				out_side = 0x08; //阴极 列(低位在上) P23启用低位,P24启用高位
				for(i=0;i<16;i++){	// 循环16个行码的地址
					//P2=out_side;
					l_high_char_half = *(TAB_addr[num]+2*i) << offset; //高位左移
					l_high_char_filled = l_high_char_half + (*(TAB_addr[num] + 2*i+1) >>(8-offset)); // 左移后补充低位的移位部分
					l_low_char_half = *(TAB_addr[num]+2*i+1) << offset; //低位左移
					l_low_char_filled = l_low_char_half + (*(TAB_addr[num+1]+2*i) >>(8-offset)); // 左移后补充下个字符的高位移位部分

					r_high_char_half = *(TAB_addr[num+1]+2*i) << offset; //阵列2高位左移
					r_high_char_filled = r_high_char_half + (*(TAB_addr[num+1]+2*i+1) >>(8-offset)); // 左移后补充阵列2低位的移位部分
					r_low_char_half = *(TAB_addr[num+1]+2*i+1) << offset; //低位左移
					r_low_char_filled = r_low_char_half + (*(TAB_addr[num+2]+2*i) >>(8-offset)); // 左移后补充下个字符的高位移位部分

					if(num == (TAB_length-2)){
						// 阵列2打印倒数字时不显示补位
						r_low_char_filled = r_low_char_half; // 阵列2低位不使用补位部分(没有下个字符了)
					}

					if(num == (TAB_length-1)){
						// 打印最后字时不显示补位
						l_low_char_filled = l_low_char_half; // 低位不使用补位部分(没有下个字符了)
						r_high_char_filled = r_low_char_filled = 0x00;	// 阵列2段码已移动至阵列1,不输出
					}
					
					row_data_send_byte(l_high_char_filled,l_low_char_filled,
						r_high_char_filled,r_low_char_filled,out_side);	   //阳极,行(高位在左)

					out_side++;
					delay_ms(scan_delay);
				}
			}
			P2=0x00; // 消隐
			delay_ms(5);
		}

		for(offset=0;offset<8;offset++){   // 列偏移量循环 单字后8位
			for(time=0;time<stay_length;time++){
				if (mode!=0) return;  // 显示模式切换后退出显示
				if (STAY_CHANGE==1){
					stay_length = stay_ms / (scan_delay * 16);
					STAY_CHANGE = 0;
				}
				out_side = 0x08; //阴极 列(低位在上) P23启用低位,P24启用高位
				for(i=0;i<16;i++){	// 循环16个行码的地址
					//P2=out_side;
					l_high_char_half = *(TAB_addr[num] + 2*i+1) << offset; //高位左移
					l_high_char_filled = l_high_char_half + (*(TAB_addr[num+1]+2*i) >>(8-offset)); // 左移后补充低位的移位部分
					l_low_char_half = *(TAB_addr[num+1]+2*i) << offset; //低位左移
					l_low_char_filled = l_low_char_half + (*(TAB_addr[num+1]+2*i+1) >>(8-offset)); // 左移后补充下个字符的高位移位部分
					
					r_high_char_half = *(TAB_addr[num+1]+2*i+1) << offset; //阵列2高位左移
					r_high_char_filled = r_high_char_half + (*(TAB_addr[num+2]+2*i) >>(8-offset)); // 左移后补充阵列2低位的移位部分
					r_low_char_half = *(TAB_addr[num+2]+2*i) << offset; //低位左移
					r_low_char_filled = r_low_char_half + (*(TAB_addr[num+2]+2*i+1) >>(8-offset)); // 左移后补充下个字符的高位移位部分

					if(num == (TAB_length-2)){
						// 阵列2打印倒数字时不显示补位
						r_high_char_filled = r_high_char_half; //  阵列2高位不使用补位部分(输出的段码已经是低位段码了)
						r_low_char_filled = 0x00; // 阵列2低位不使用补位部分(没有下个字符了)
					}
					
					if(num == (TAB_length-1)){
						// 打印最后字时不显示补位
						l_high_char_filled = l_high_char_half; //  高位不使用补位部分(输出的段码已经是低位段码了)
						l_low_char_filled = 0x00;				// 字符的低位段码已移动至高位区,低位不输出
						r_high_char_filled = r_low_char_filled = 0x00;	// 阵列2段码已移动至阵列1,不输出
					}

					row_data_send_byte(l_high_char_filled,l_low_char_filled,
						r_high_char_filled,r_low_char_filled,out_side);	//阳极,行(高位在左)
					out_side++;
					delay_ms(scan_delay);
				}
			}
			P2=0x00; // 消隐
			delay_ms(5);
		}
	}
}

void double_up_move(){
// 阵列整行上移	 模式1
	u8 out_side;	
	int i;
	int TAB_index;
	int offset;
	int time;	
	int stay_length  = stay_ms / (scan_delay * 16); //内容驻留循环次数(分子值单位ms)
	u8 l_high_char,l_low_char,r_high_char,r_low_char; 

	for(TAB_index=0;TAB_index<TAB_length;TAB_index=TAB_index+2){    // 循环 字符数组的下标
		for(offset=0;offset<16;offset++){   // 行偏移量循环
			for(time=0;time<stay_length;time++){
				if (mode!=1) return;   // 显示模式切换后退出显示
				if (STAY_CHANGE==1){
					stay_length = stay_ms / (scan_delay * 16);
					STAY_CHANGE = 0;
				}
				out_side = 0x08; //阴极 列(低位在上) P23启用低位,P24启用高位
				for(i=0;i<16;i++){	// 循环16个行码的地址
					//P2=out_side;
					if(TAB_index == (TAB_length-1) && i >= (15 - offset)){
						out_side=0x00; // 打印最后字时不显示超出行
					}
					
					l_high_char = *(TAB_addr[TAB_index]+2*(offset+i));
					l_low_char  = *(TAB_addr[TAB_index]+2*(offset+i)+1);
					r_high_char = *(TAB_addr[TAB_index+1]+2*(offset+i));
					r_low_char = *(TAB_addr[TAB_index+1]+2*(offset+i)+1);

				   if(offset>0 && i > (15 - offset)){
				   		// 单阵列显示补位段码时跳过一个字
						l_high_char = *(TAB_addr[TAB_index+1]+2*(offset+i));  //相对偏移量=一个字的行数(字符指针偏移一个单位)
						l_low_char  = *(TAB_addr[TAB_index+1]+2*(offset+i)+1);
						r_high_char = *(TAB_addr[TAB_index+2]+2*(offset+i));
						r_low_char = *(TAB_addr[TAB_index+2]+2*(offset+i)+1);
						if(TAB_index+4>TAB_length && i >= (15 - offset)){ //(TAB_index+3)>(TAB_length-1)
							// 右阵列显示倒数第二个字时,不显示补位段码(没有下一行的字了)
								r_high_char	= r_low_char = 0x00;
						}
						if(TAB_index+3>TAB_length && i >= (15 - offset)){ //(TAB_index+2)>(TAB_length-1)
							// 左阵列显示倒数第二个字时,不显示补位段码(没有下一行的字了)
								l_high_char	= l_low_char = 0x00;
						}
				   }
				   	if(TAB_index+2>TAB_length){	//(TAB_index+1)>(TAB_length-1)
						// 最后一个字不在右阵列时,右阵列不显示 
						r_high_char	= r_low_char = 0x00;
					}

					row_data_send_byte(l_high_char,l_low_char,
						r_high_char,r_low_char,out_side); //阳极,行(高位在左)
					out_side++;
					delay_ms(scan_delay);
				}
			}
			P2=0x00; // 消隐
		}
	}

}

void flicker_display(){
// 闪烁	 模式2
	u8 out_side;	
	int i;
	int num;
	int time;
	int display_ms = 200;	 //文字闪烁间隔时长(ms)
	int display_length = display_ms / (scan_delay * 16); // 文字闪烁循环次数
	int stay_length  = stay_ms / display_ms / 2 + 2; //内容驻留循环次数(分子值单位ms)
	
	int show_i;		  //文字闪烁循环计数变量
	u8 l_high_char,l_low_char,r_high_char,r_low_char; 

	for(num=0;num<TAB_length;num=num+2){    // 循环 字符数组的下标

		for(time=0;time<stay_length;time++){
			if (mode!=2) return;   // 显示模式切换后退出显示
			if (STAY_CHANGE==1){
				stay_length  = stay_ms / (2*display_ms);
				STAY_CHANGE = 0;
			}
			for(show_i=0;show_i<display_length;show_i++){
				out_side = 0x08; //阴极 列(低位在上) P23启用低位,P24启用高位
				for(i=0;i<32;i=i+2){	// 循环16个行码的地址
					l_high_char = *(TAB_addr[num]+i);
					l_low_char  = *(TAB_addr[num]+i+1);
					r_high_char = *(TAB_addr[num+1]+i);
					r_low_char = *(TAB_addr[num+1]+i+1);
	
				   	if(num+2>TAB_length){
						// 最后一个字不在右阵列时,右阵列不显示
						r_high_char	= r_low_char = 0x00;
					}
	
					row_data_send_byte(l_high_char,l_low_char,
						r_high_char,r_low_char,out_side); //阳极,行(高位在左)
					out_side++;
					delay_ms(scan_delay);
				}	
			}
			for(show_i=0;show_i<display_length;show_i++){
				//不输出信号
				row_data_send_byte(0x00,0x00,0x00,0x00,0x00);
				for(i=0;i<16;i++){
					delay_ms(scan_delay);
				}
			}
			
		}
		P2=0x00; // 消隐
		
	}

}


void static_switch(){
// 交替显示	 模式3
	u8 out_side;	
	int i;
	int num;
	int time;	
	int stay_length  = stay_ms / (scan_delay * 16); //内容驻留循环次数(分子值单位ms)
	u8 l_high_char,l_low_char,r_high_char,r_low_char; 

	for(num=0;num<TAB_length;num=num+2){    // 循环 字符数组的下标

		for(time=0;time<stay_length;time++){
			if (mode!=3) return;   // 显示模式切换后退出显示
			if (STAY_CHANGE==1){
				stay_length  = stay_ms / (scan_delay * 16);
				STAY_CHANGE = 0;
			}
			out_side = 0x08; //阴极 列(低位在上) P23启用低位,P24启用高位
			for(i=0;i<32;i=i+2){	// 循环16个行码的地址

				l_high_char = *(TAB_addr[num]+i);
				l_low_char  = *(TAB_addr[num]+i+1);
				r_high_char = *(TAB_addr[num+1]+i);
				r_low_char = *(TAB_addr[num+1]+i+1);

			   	if(num+2>TAB_length){
					// 最后一个字不在右阵列时,右阵列不显示
					r_high_char	= r_low_char = 0x00;
				}

				row_data_send_byte(l_high_char,l_low_char,
					r_high_char,r_low_char,out_side); //阳极,行(高位在左)
				out_side++;
				delay_ms(scan_delay);
			}
		}
		P2=0x00; // 消隐
		
	}

}


void main(){
	IE = 0x85; //启用中断INT0和INT1 1000 0101
	TCON = 0x05; //选择跳变触发方式 0000 0101
	
	while (1){
		if(mode==0){
			left_move();
		}else if(mode==1){
			double_up_move();
		}else if(mode==2){
			flicker_display();
		}else if(mode==3){
			static_switch();
		}
	}
}

void INT0_change_print_mode() interrupt 0
{// 切换模式
	mode++;
	if(mode>3) mode=0;
}

void INT1_change_print_mode() interrupt 2
{// 切换内容停留时长
	if(stay_ms>1000) stay_ms=250;
	stay_ms = stay_ms + 250;
 	STAY_CHANGE = 1;
}


